package com.automannn.sso.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.jasig.cas.client.validation.*;
import org.jasig.cas.client.validation.json.Cas30JsonServiceTicketValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collections;

/**
 * @author automannn
 * @Date 2025/6/29
 */
@Configuration
@Profile("cas")
public class CasSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private CasAuthenticationProvider casAuthenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers( "/public/**").permitAll() // 公开URL
                .anyRequest().authenticated() // 其他所有URL需要认证
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint) // 使用CAS入口点
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/public/") // 注销后跳转
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .and()
                .cors(cors->cors.configurationSource(corsConfigurationSource()))
                .csrf().disable(); // CAS协议可能涉及跨站请求，根据实际情况决定是否禁用CSRF

        // 添加CAS过滤器链
        http.addFilterAt(casFilter(), CasAuthenticationFilter.class);
    }

    private CorsConfigurationSource corsConfigurationSource() {
        return request -> {
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowedOrigins(Collections.singletonList("*"));
            config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
            config.setAllowedHeaders(Arrays.asList("Content-Type", "Authorization","atm_token","currenturi","allowcredentials"));
            config.setExposedHeaders(Arrays.asList("atm_token","currenturi","allowcredentials"));
            config.setAllowCredentials(true);
            config.setMaxAge(3600L); // 设置与 OPTIONS 缓存有关
            return config;
        };
    }

    // 配置处理ST的过滤器（位于 /login/cas）
    private CasAuthenticationFilter casFilter() throws Exception {
        CasAuthenticationFilter filter = new CasAuthenticationFilter(){
            @Override
            protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
                //放行预检请求
                if ("OPTIONS".equals(request.getMethod())) {
                    return false;
                }
                return super.requiresAuthentication(request, response);
            }
        };
        filter.setServiceProperties(serviceProperties());
        filter.setAuthenticationManager(authenticationManager());

        // 如果CAS服务器重定向回来的URL不是默认的/login/cas，需要设置filter.setFilterProcessesUrl("/your-custom-url");
        return filter;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(casAuthenticationProvider);
    }

    @Bean
    //定义应用（服务）在 CAS 服务器上的注册信息。
    public ServiceProperties serviceProperties() {
        ServiceProperties serviceProperties = new ServiceProperties();
        // 应用接收ST的完整URL，必须与CAS服务器注册的服务URL一致
        serviceProperties.setService(getApplicationContext().getEnvironment().getProperty("cas.client.service"));
        // 通常设为false。true表示强制重新登录（即使有SSO会话），用于敏感操作
        serviceProperties.setSendRenew(false);
        return serviceProperties;
    }

    @Bean
    //定义当需要认证时如何开始 CAS 流程
    public AuthenticationEntryPoint authenticationEntryPoint(ServiceProperties serviceProperties) {
        CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint();
        // CAS服务器的登录URL
        entryPoint.setLoginUrl(getApplicationContext().getEnvironment().getProperty("cas.client.casServerUrlLogin"));
        entryPoint.setServiceProperties(serviceProperties);
        return entryPoint;
    }

    @Bean
    //定义如何验证从 CAS 服务器收到的 Service Ticket (ST)
    public TicketValidator ticketValidator() {
        // CAS服务器的基础URL
        return new Cas30JsonServiceTicketValidator(getApplicationContext().getEnvironment().getProperty("cas.client.casServerUrl")){
            @Override
            protected Assertion parseResponseFromServer(String response) throws TicketValidationException {
                JSONObject jsonObject = JSON.parseObject(response);
                return new AssertionImpl(jsonObject.getString("username"));
            }
        };
    }

    @Bean
    //核心处理器，负责验证 ST、获取用户信息并构建 Authentication 对象
    public CasAuthenticationProvider casAuthenticationProvider(
            ServiceProperties serviceProperties,
            TicketValidator ticketValidator,
            UserDetailsService userDetailsService) {

        CasAuthenticationProvider provider = new CasAuthenticationProvider();
        provider.setServiceProperties(serviceProperties);
        provider.setTicketValidator(ticketValidator);
        // 关键：用于根据CAS返回的用户名加载用户详细信息和权限
        provider.setUserDetailsService(userDetailsService);
        // 设置一个唯一的key（任意字符串），用于区分Provider实例
        provider.setKey(getApplicationContext().getEnvironment().getProperty("cas.client.serviceId"));
        return provider;
    }

    @Bean
    public UserDetailsService userDetailsService() {
        // 示例：使用内存用户存储。实际项目中通常连接数据库或LDAP
        UserDetails user = User.withUsername("admin")
                .password("") // CAS认证，本地密码通常为空或不重要，但字段不能为null
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
}
